package com.ifnk.portaljava.dapWeb.dapweb;
/**
 * <p>Title: 命令封装类</p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2010</p>
 *
 * <p>Company: </p>
 *
 * @author WenSheng.Song.hlyt@gmail.com
 * @version 1.0
 */

import com.ifnk.portaljava.dapWeb.common.Constants;
import com.ifnk.portaljava.dapWeb.dataPacket.TypeDefine;
import com.ifnk.portaljava.dapWeb.hlyt.servlet.CallTraceServlet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.math.BigInteger;
import java.nio.file.ClosedWatchServiceException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;

public class Command extends PacketHeader {
    //	private int commandID;
    private short commandType; /* COMMAND, ACK, NACK */
    private String sessionId;
    private int startTime;
    private int endTime;
    private int maxDataNumber;
    private String str_protocol;
    private String svrList;
    private String condition;
    private int cause;
    private int xmlLen;/* xmllen */
    private String cdr;
    private byte char_protocol;
    private int svrId;
    private int cdrId;
    private byte cdrEndFlag;
    private int sequenceNo;
    private byte[] pkt;
    private byte sessionIdLen;
    private int receiveCount = 100000;
    private Log loger = LogFactory.getLog(Command.class);

    public Command() {
        super();
    }

    public short getCommandType() {
        return commandType;
    }

    public void setCommandType(short commandType) {
        this.commandType = commandType;
    }

    public String getSessionId() {
        return sessionId;
    }

    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }

    public int getStartTime() {
        return startTime;
    }

    public void setStartTime(int startTime) {
        this.startTime = startTime;
    }

    public int getEndTime() {
        return endTime;
    }

    public void setEndTime(int endTime) {
        this.endTime = endTime;
    }

    public int getMaxDataNumber() {
        return maxDataNumber;
    }

    public void setMaxDataNumber(int maxDataNumber) {
        this.maxDataNumber = maxDataNumber;
    }

    public String getStr_protocol() {
        return str_protocol;
    }

    public void setStr_protocol(String str_protocol) {
        this.str_protocol = str_protocol;
    }

    public String getSvrList() {
        return svrList;
    }

    public void setSvrList(String svrList) {
        this.svrList = svrList;
    }

    public String getCondition() {
        return condition;
    }

    public void setCondition(String condition) {
        this.condition = condition;
    }

    public int getCause() {
        return cause;
    }

    public void setCause(int cause) {
        this.cause = cause;
    }

    public int getXmlLen() {
        return xmlLen;
    }

    public void setXmlLen(int xmlLen) {
        this.xmlLen = xmlLen;
    }

    public String getCdr() {
        return cdr;
    }

    public void setCdr(String cdr) {
        this.cdr = cdr;
    }

    public byte getChar_protocol() {
        return char_protocol;
    }

    public void setChar_protocol(byte char_protocol) {
        this.char_protocol = char_protocol;
    }

    public int getSvrId() {
        return svrId;
    }

    public void setSvrId(int svrId) {
        this.svrId = svrId;
    }

    public int getCdrId() {
        return cdrId;
    }

    public void setCdrId(int cdrId) {
        this.cdrId = cdrId;
    }

    public byte getCdrEndFlag() {
        return cdrEndFlag;
    }

    public void setCdrEndFlag(byte cdrEndFlag) {
        this.cdrEndFlag = cdrEndFlag;
    }

    public int getSequenceNo() {
        return sequenceNo;
    }

    public void setSequenceNo(int sequenceNo) {
        this.sequenceNo = sequenceNo;
    }

    public byte[] getPkt() {
        return pkt;
    }

    public void setPkt(byte[] pkt) {
        this.pkt = pkt;
    }

    public byte getSessionIdLen() {
        return sessionIdLen;
    }

    public void setSessionIdLen(byte sessionIdLen) {
        this.sessionIdLen = sessionIdLen;
    }

    public int getReceiveCount() {
        return receiveCount;
    }

    public void setReceiveCount(int receiveCount) {
        this.receiveCount = receiveCount;
    }


    public int EncodeWWWCTStartCommand(byte[] packet, int size, int cmdid)
            throws Exception {
        return EncodeStartCommand(packet, size, cmdid);
    }

    public int EncodeWWWCQStartCommand(byte[] packet, int size, int cmdid)
            throws Exception {
        return EncodeStartCommand(packet, size, cmdid);
    }

    private int EncodeStartCommand(byte[] packet, int size, int cmdid)
            throws Exception {
//		setCommandID(cmdid);
        //初始化包头
        setType((byte) (cmdid >> 8 & 0xFF));
        setSubType((byte) (cmdid & 0xFF));
        setTs_s((int) System.currentTimeMillis() / 1000);
        setTs_ns((int) (System.currentTimeMillis() / 1000 * 1000000));
        Encode(packet, size);

        int off = 20;
        packet[off] = TypeDefine.sessionIdLen;
        off++;
        off += EncodeStringWithLength(packet, off, size, sessionId);
        if (cmdid == TypeDefine.WWW_CQ_START) {
            off += EncodeInt(packet, off, size, startTime);
            off += EncodeInt(packet, off, size, endTime);
            //* 加上返回最大值和解压标志
            off += EncodeInt(packet, off, size, maxDataNumber);
            if (Constants.zipFlag) {
                packet[off] = (byte) (1 & 0xFF);
            } else {
                packet[off] = (byte) (0 & 0xFF);
            }

            off++;
            //*/
        }
        off += EncodeNullTerminatedString(packet, off, size, str_protocol);
        off += EncodeNullTerminatedString(packet, off, size, svrList);
        off += EncodeNullTerminatedString(packet, off, size, condition);
        SetLength(packet, off);

        return off;
    }

    public int EncodeRawQuery(byte[] packet, int size, int cmdid)
            throws Exception {

        setType((byte) (cmdid >> 8 & 0xFF));
        setSubType((byte) (cmdid & 0xFF));
        Encode(packet, size);

        int off = 20;
        packet[off] = TypeDefine.sessionIdLen;
        off++;
        off += EncodeStringWithLength(packet, off, size, sessionId);
        packet[off] = this.char_protocol;
        off++;
        off += EncodeInt(packet, off, size, svrId);
        off += EncodeInt(packet, off, size, cdrId);
        SetLength(packet, off);
        return off;
    }

    public int EncodeQueryQuery(byte[] packet, int size, int cmdid, int cdrs[], int times[])
            throws Exception {

        setType((byte) (cmdid >> 8 & 0xFF));
        setSubType((byte) (cmdid & 0xFF));
        Encode(packet, size);

        int off = 20;
        packet[off] = TypeDefine.sessionIdLen;
        off++;
        off += EncodeStringWithLength(packet, off, size, sessionId);

        // now add cdr and time

        for (int i = 0; i < cdrs.length; i++) {
            if (cdrs[i] != 0) {
                off += EncodeInt(packet, off, size, times[i]);
                off += EncodeInt(packet, off, size, cdrs[i]);
            }
        }

        SetLength(packet, off);
        return off;
    }


    public int EncodeQueryQuery(byte[] packet, int size, int cmdid, int zdrs[])
            throws Exception {

        setType((byte) (cmdid >> 8 & 0xFF));
        setSubType((byte) (cmdid & 0xFF));
        Encode(packet, size);

        int off = 20;
        packet[off] = TypeDefine.sessionIdLen;
        off++;
        off += EncodeStringWithLength(packet, off, size, sessionId);

        // now add cdr and time
        System.out.println("off before = " + off);

        System.out.println("packet[off+5] = " + packet[off + 5]);
//        if (CallTraceServlet.getXdrLen() == 16) {
        for (int i = 0; i < zdrs.length; i += 6) {
            off += EncodeInt(packet, off, size, zdrs[i]);
            // xdr interface type
            packet[off++] = (byte) zdrs[i + 1];

            off += EncodeInt(packet, off, size, zdrs[i + 2]);
            off += EncodeInt(packet, off, size, zdrs[i + 3]);
            off += EncodeInt(packet, off, size, zdrs[i + 4]);
            off += EncodeInt(packet, off, size, zdrs[i + 5]);
        }

//        } else {
//            for (int i = 0; i < zdrs.length; i++) {
//                off += EncodeInt(packet, off, size, zdrs[i]);
//                System.out.println("off = " + off);
//            }
//        }


        SetLength(packet, off);
        return off;
    }


    public int EncodeStopCommand(byte[] packet, int size, int cmdid)
            throws Exception {

        setType((byte) (cmdid >> 8 & 0xFF));
        setSubType((byte) (cmdid & 0xFF));
        setTs_s((int) System.currentTimeMillis() / 1000);
        setTs_ns((int) (System.currentTimeMillis() / 1000 * 1000000));
        Encode(packet, size);

        int off = 20;
        packet[off] = TypeDefine.sessionIdLen;
        off++;
        off += EncodeStringWithLength(packet, off, size, sessionId);
        off += EncodeInt(packet, off, size, cause);
        SetLength(packet, off);

        return off;
    }


    public int EncodeHeartBeat(byte[] packet, int size, int cmdid)
            throws Exception {

        setType((byte) (cmdid >> 8 & 0xFF));
        setSubType((byte) (cmdid & 0xFF));
        setTs_s((int) System.currentTimeMillis() / 1000);
        setTs_ns((int) (System.currentTimeMillis() / 1000 * 1000000));
        Encode(packet, size);

        int off = 20;
        packet[off] = TypeDefine.sessionIdLen;
        off++;
        off += EncodeStringWithLength(packet, off, size, sessionId);
        SetLength(packet, off);
        return off;
    }

    private int EncodeStringWithLength(byte[] packet, int offset, int size,
                                       String str) throws Exception {
        if (offset + str.getBytes().length > size) {
            loger.error("WebServer EncodeStringWithLength function offset > size");
            throw new Exception("WebServer EncodeStringWithLength function offset > size");
        }
        System.arraycopy(str.getBytes(), 0, packet, offset,
                str.getBytes().length);
        return str.getBytes().length;
    }

    // 有结束表示符的字符串编码
    private int EncodeNullTerminatedString(byte[] packet, int offset, int size,
                                           String str) throws Exception {
        if (offset + str.getBytes().length > size) {
            loger.error("WebServer EncodeNullTerminatedString function offset > size");
            throw new Exception("WebServer EncodeNullTerminatedString function offset > size");
        }
        System.arraycopy(str.getBytes(), 0, packet, offset,
                str.getBytes().length);
        return str.getBytes().length;
    }

    private int EncodeInt(byte[] packet, int offset, int size, int value)
            throws Exception {
        if ((offset + 4) > size) {
            loger.error("WebServer EncodeInt function offset > size");
            throw new Exception("WebServer EncodeInt function offset > size");
        }
        EncodeIntToByte(packet, offset, value);
        return 4;
    }


    // start命令的应答包
    public void DecodeStartCommand(byte[] packet) {
        DecodeHeader(packet);
        int off = 20;
        sessionIdLen = packet[off];
        off++;
        sessionId = new String(packet, off, sessionIdLen);
        off += sessionIdLen;
        cause = DecodeByteArrayToInt(packet, off);
    }

    public void DecodeStopCommand(byte[] packet) {
        DecodeStartCommand(packet);
    }

    public void DecodeCDRData(byte[] packet) throws Exception {
        //if(压缩标志存在，则进行解压)
        if (Constants.zipFlag) {
            packet = Decompresser(packet);
        }
        DecodeHeader(packet);
        int off = 20;
        sessionIdLen = packet[off];
        off++;
        sessionId = new String(packet, off, sessionIdLen);
        off += sessionId.length();
        xmlLen = this.DecodeByteArrayToInt(packet, off);
        off += 4;
        if (xmlLen <= 0)
            throw new Exception("WebServer DecodeCDRData cdr Len <=0");
        cdr = new String(packet, off, xmlLen);
    }

    public void DecodeRawDataII(byte[] packet) throws Exception {

        DecodeHeader(packet);
        int t = ((this.getType() & 0xff) << 8) | (this.getSubType() & 0xff);
        if (t == TypeDefine.WWW_RQ_RAWDATA) {
            this.DecodeQueryQueryData(packet);
        } else {  // means WWW_CQ_RAWDATA or WWW_CT_RAWDATA
            this.DecodeRawData(packet);
        }

    }

    public void DecodeRawDataIII(byte[] packet) throws Exception {

        DecodeHeader(packet);
        int t = ((this.getType() & 0xff) << 8) | (this.getSubType() & 0xff);
        if (t == TypeDefine.WWW_RQ_RAWDATA) {
            this.DecodeQueryQueryDataIII(packet);
        } else {  // means WWW_CQ_RAWDATA or WWW_CT_RAWDATA
            this.DecodeRawData(packet);
        }

    }


    public void DecodeRawData(byte[] packet) throws Exception {
        DecodeHeader(packet);
        int off = 20;
        sessionIdLen = packet[off];
        off++;
        sessionId = new String(packet, off, sessionIdLen);
        off += sessionId.length();
        char_protocol = packet[off];
        off++;
        svrId = DecodeByteArrayToInt(packet, off);
        off += 4;
        cdrId = DecodeByteArrayToInt(packet, off);
        off += 4;
        cdrEndFlag = packet[off];
        loger.debug("WebServer DecodeRawData cdrEndFlag=" + Integer.toHexString(packet[off] & 0xFF)
                + " off=" + off);
        off++;
        sequenceNo = DecodeByteArrayToInt(packet, off);
        off += 4;
        if ((cdrEndFlag != 1) && ((packet.length - off) <= 0)) {
            throw new Exception("WebServer DecodeRawData pkt_len <= 0 && cdrEndFlag=" + cdrEndFlag);
        }
        byte[] cdr_pkt = new byte[packet.length - off];
        System.arraycopy(packet, off, cdr_pkt, 0, packet.length - off);
        pkt = cdr_pkt;
    }

    public void DecodeQueryQueryData(byte[] packet) throws Exception {
        //DecodeHeader(packet);
        int off = 20;
        sessionIdLen = packet[off];
        off++;
        sessionId = new String(packet, off, sessionIdLen);
        off += sessionId.length();


        startTime = DecodeByteArrayToInt(packet, off);
        off += 4;
        this.setTs_s(startTime);

        cdrId = DecodeByteArrayToInt(packet, off);
        off += 4;

        cdrEndFlag = packet[off++];

        sequenceNo = DecodeByteArrayToInt(packet, off);
        off += 4;

        if ((cdrEndFlag != 1) && ((packet.length - off) <= 0)) {
            throw new Exception("WebServer DecodeRawData pkt_len <= 0 && cdrEndFlag=" + cdrEndFlag);
        }

        byte[] cdr_pkt = new byte[packet.length - off];
        System.arraycopy(packet, off, cdr_pkt, 0, packet.length - off);
        pkt = cdr_pkt;

    }

    public void DecodeQueryQueryDataIII(byte[] packet) throws Exception {
        //DecodeHeader(packet);
        int off = 20;
        sessionIdLen = packet[off];
        off++;
        sessionId = new String(packet, off, sessionIdLen);
        off += sessionId.length();
		
		
		/*
		startTime = DecodeByteArrayToInt(packet, off);
		off += 4;
		this.setTs_s(startTime);
		
		cdrId = DecodeByteArrayToInt(packet, off);
		
		*/

        byte[] bb = new byte[16];
        System.arraycopy(packet, off, bb, 0, 16);

        BigInteger bi = new BigInteger(bb);

        off += 16;

        cdrId = bi.intValue();
        cdr = bi.toString(16);

        cdrEndFlag = packet[off++];

        sequenceNo = DecodeByteArrayToInt(packet, off);
        off += 4;

        if ((cdrEndFlag != 1) && ((packet.length - off) <= 0)) {
            throw new Exception("WebServer DecodeRawData pkt_len <= 0 && cdrEndFlag=" + cdrEndFlag);
        }

        byte[] cdr_pkt = new byte[packet.length - off];
        System.arraycopy(packet, off, cdr_pkt, 0, packet.length - off);
        pkt = cdr_pkt;

    }


    public void DecodePktData(byte[] packet) throws Exception {
        DecodeHeader(packet);
        //Todo: add decode pkt here

    }

    public void DecodeHeartBeat(byte[] packet) {
        DecodeHeader(packet);
        int off = 20;
        sessionIdLen = packet[off];
        off++;
        sessionId = new String(packet, off, sessionIdLen);
    }

    private byte[] Decompresser(byte[] gzipPacket) {
        Inflater decompresser = new Inflater();
        //		System.out.println("gzipPacket.length: "+gzipPacket.length);
        decompresser.setInput(gzipPacket, 20, gzipPacket.length - 20);

        byte[] newPacket = null;
        byte[] resultPacket = new byte[512 * 1024];

        try {
            int resultLength = decompresser.inflate(resultPacket);

            newPacket = new byte[resultLength + 20];
            System.arraycopy(resultPacket, 0, newPacket, 20, resultLength);
            System.arraycopy(gzipPacket, 0, newPacket, 0, 20);
        } catch (DataFormatException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        return newPacket;
    }
	/*
	private byte[] addCapacity(byte rece[],byte desc[],int length) {

		byte[] temp = new byte[desc.length + length];
		String outputString = "";
		try {
			outputString = new String(rece, 0, rece.length, "UTF-8");
			System.out.println("addCapacityString:"+outputString);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		System.arraycopy(desc, 0, temp, 0, desc.length);
		System.arraycopy(rece, 0, temp, desc.length, length);
		return temp;
	}
	 */

    public static Command cloneme(Command c) {
        if (c == null)
            return null;

        Command clone = new Command();
        clone.setCdrId(c.getCdrId());
        clone.setCdrEndFlag(c.getCdrEndFlag());
        clone.setCommandType(c.getCommandType());

        if (c.getPkt() != null) {
            byte[] pkt = new byte[c.getPkt().length];
            System.arraycopy(c.getPkt(), 0, pkt, 0, pkt.length);
            clone.setPkt(pkt);
        }

        clone.setProtocol(c.getProtocol());
        clone.setSequenceNo(c.getSequenceNo());
        clone.setSessionId(c.getSessionId());
        clone.setStartTime(c.getStartTime());

        clone.setSubType(c.getSubType());
        clone.setTs_ns(c.getTs_ns());
        clone.setTs_s(c.getTs_s());
        clone.setType(c.getType());

        return clone;
    }

}